<?php
declare(strict_types=1);
/**
 * Created by project.
 * User: huanzipap
 * Date: 2023/5/30
 * Time: 9:58
 */
namespace App\Libs;

use Hyperf\Di\Annotation\Inject;
use Hyperf\HttpServer\Contract\RequestInterface;
use Hyperf\HttpServer\Contract\ResponseInterface;
use Hyperf\Context\Context;
use Psr\Http\Message\ServerRequestInterface;

class Common
{
    #[Inject] protected RequestInterface $request;
    #[Inject] protected ResponseInterface $response;

    /**
     * 移除字符串中的emoji表情
     * @param string $str
     * @return string
     */
    public function removeEmojiChar(string $str): string
    {
        $strLen = strlen($str);
        $strArr = [];
        for ($i = 0; $i < $strLen; $i++) {
            $mbSubstr = mb_substr($str, $i, 1, 'utf-8');
            if (strlen($mbSubstr) >= 4) {
                continue;
            }
            $strArr[] = $mbSubstr;
        }
        return implode('', $strArr);
    }

    /**
     * 生成唯一订单
     * @return string
     */
    public function generateOrderSn(): string
    {
        return date('YmdHis').substr(implode( array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
    }

    /**
     * 生成唯一数
     * @return string
     */
    public function generateNum(): string
    {
        return date('YmdHis').substr(implode( array_map('ord', str_split(substr(uniqid(), 7, 13), 1))), 0, 8);
    }


    /**
     * 生成随机card券
     * @param int $letterCount
     * @param int $numCount
     * @param int $step
     * @param string $link_tip
     * @return string
     */
    public function generateCardCode(int $letterCount = 6,int $numCount = 6,int $step = 4,string $link_tip='-'):string
    {
        //字母数组
        $letterArr = ['A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z'];
        $numArr = [0,1,2,3,4,5,6,7,8,9];

        $randLetter = [];
        if($letterCount>0){
            $randLetterValue = array_rand($letterArr,$letterCount);
            if(is_array($randLetterValue)){
                $randLetterIndex = $randLetterValue;
                foreach ($randLetterIndex as $rliKey=>$rliVal){
                    $randLetter[$rliKey] = $letterArr[$rliVal];
                }
            }else{
                $randLetter[0] = $letterArr[$randLetterValue];
            }
        }else{
            $randLetter = $letterArr;
        }

        $randNum = [];
        if($numCount>0){
            $randNumValue = array_rand($numArr,$numCount);
            if(is_array($randNumValue)){
                $randNumIndex = $randNumValue;
                foreach ($randNumIndex as $rniKey=>$rniVal){
                    $randNum[$rniKey] = $numArr[$rniVal];
                }
            }else{
                $randNum[0] = $numArr[$randNumValue];
            }
        }else{
            $randNum = $numArr;
        }

        //券数组
        $cardArr = [];
        $i=0;
        if(count($randNum)>0){
            foreach ($randNum as $nKey=>$nVal){
                $cardArr[$i++] = $nVal;
            }
        }
        if(count($randLetter)>0){
            foreach ($randLetter as $nKey=>$nVal){
                $cardArr[$i++] = $nVal;
            }
        }
        //php数组随机排序
        shuffle($cardArr);

        //优惠券卡号
        $cardCode = '';
        if($step>0){
            $c=1;
            foreach ($cardArr as $aKey=>$aVal){
                if($aKey==$c*$step){
                    $cardCode.=$link_tip . $aVal;
                    $c++;
                }else{
                    $cardCode.= $aVal;
                }

            }
        }else{
            $cardCode = implode('',$cardArr);
        }
        return $cardCode;
    }


    /**
     * 验证手机号
     * @param string $phone
     * @return array
     */
    public function checkPhone(string $phone): array
    {
        if(empty($phone)){
            return ['status'=>false,'message'=>'手机账号不能为空!'];
        }else{
            if(!preg_match('/1{1}\d{10}$/', $phone)){
                return ['status'=>false,'message'=>'手机格式不正确!'];
            }
        }
        return ['status'=>true,'message'=>'success'];
    }


    /**
     * 获取客户端ip地址
     * @return string
     */
    public function getClientIp(): string
    {
        $request = Context::get(ServerRequestInterface::class);
        $ip_addr = $request->getHeaderLine('x-forwarded-for');
        if (verify_ip($ip_addr)) {
            return $ip_addr;
        }
        $ip_addr = $request->getHeaderLine('remote-host');
        if (verify_ip($ip_addr)) {
            return $ip_addr;
        }
        $ip_addr = $request->getHeaderLine('x-real-ip');
        if (verify_ip($ip_addr)) {
            return $ip_addr;
        }
        $ip_addr = $request->getServerParams()['remote_addr'] ?? '0.0.0.0';
        if (verify_ip($ip_addr)) {
            return $ip_addr;
        }
        return '0.0.0.0';
    }



    /**
     * 功能：循环检测并创建文件夹
     * @param string $path
     * @return void
     */
    public function createDir(string $path):void
    {
        if (!file_exists($path)){
            $this->createDir(dirname($path));
            mkdir($path, 0777); //检查是否有该文件夹，如果没有就创建，并给予最高权限
        }
    }



    /**
     * Html转换为TextArea文本
     * @param string $str
     * @return string
     */
    public function htmlToText(string $str):string
    {
        if ($str == null) {
            return "";
        }else if (strlen($str) == 0) {
            return "";
        }
        $str = str_replace("<br />", "\n",$str);
        $str = str_replace("<br />", "\r",$str);
        return $str;
    }



    /**
     * TextArea文本转换为Html:写入数据库时使用
     * @param string $str
     * @return string
     */
    public function textToHtml(string $str):string {
        if ($str == null) {
            return "";
        }else if (strlen($str) == 0) {
            return "";
        }

        $str = str_replace("\n", "<br />",$str);
        $str = str_replace("\r", "<br />",$str);
        return $str;
    }



    /**
     * 对字符串进行加密。
     * @param string $txt
     * @param string $key
     * @return string
     */
    public function encryptString(string $txt,string $key='huanzipap'):string
    {
        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-=+";
        $nh = rand(0,64);
        $ch = $chars[$nh];
        $mdKey = md5($key.$ch);
        $mdKey = substr($mdKey,$nh%8, $nh%8+7);
        $txt = base64_encode($txt);
        $tmp = '';
        $k=0;
        for($i=0; $i<strlen($txt); $i++) {
            $k = $k == strlen($mdKey) ? 0 : $k;
            $j = ($nh+strpos($chars,$txt[$i])+ord($mdKey[$k++]))%64;
            $tmp .= $chars[$j];
        }
        return urlencode($ch.$tmp);
    }



    /**
     * 对字符串进行解密。
     * @param string $txt
     * @param string $key
     * @return string
     */
    public function decryptString(string $txt,string $key='huanzipap'):string
    {
        $txt = urldecode($txt);
        $chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-=+";
        $ch = $txt[0];
        $nh = strpos($chars,$ch);
        $mdKey = md5($key.$ch);
        $mdKey = substr($mdKey,$nh%8, $nh%8+7);
        $txt = substr($txt,1);
        $tmp = '';
        $k = 0;
        for ($i=0; $i<strlen($txt); $i++) {
            $k = $k == strlen($mdKey) ? 0 : $k;
            $j = strpos($chars,$txt[$i])-$nh - ord($mdKey[$k++]);
            while ($j<0) $j+=64; $tmp .= $chars[$j];
        }
        return base64_decode($tmp);
    }



    /**
     * 获取m、n之间的随机数，保留2位小数
     * @param float $min
     * @param float $max
     * @return float
     */
    public function getRandNumMinRangeMax(float $min, float $max):float
    {
        if ($min > $max) {
            $numMax = $min;
            $numMin = $max;
        } else {
            $numMax = $max;
            $numMin = $min;
        }
        //生成$numMin和$numMax之间的随机浮点数，保留2位小数
        $rand = $numMin + mt_rand() / mt_getrandmax() * ($numMax - $numMin);
        return floatval(number_format($rand,2));
    }



    /**
     * 把秒数转换为时分秒的格式
     * @param int $times 时间，单位 秒
     * @return string
     */
    public function secondToTime(int $times):string
    {
        $result = '00:00:00';
        if ($times>0) {
            $day = floor($times/(3600 * 24));
            $times = $times % (3600 * 24);
            $hour = floor($times/3600);
            $times = $times % 3600;
            $minute = floor($times/60);
            $times = $times % 60;
            $second = floor($times);
            if($day>0){
                $result = $day.'天'.$hour.':'.$minute.':'.$second;
            }else{
                $result = $hour.':'.$minute.':'.$second;
            }
        }
        return $result;
    }



    /**
     * list递归实现无限极分类--分层级--加索引
     * @param array $list
     * @param int $pid
     * @param string $children
     * @param int $index
     * @return array
     */
    public function recursionTree(array $list,int $pid=0,string $children='children',int $index=0):array
    {
        $tree = [];
        $count=1;
        $cateIdArr = [];
        foreach ($list as $k => &$v){
            if($pid == $v['pid']){
                if($v['level']==1){
                    $cateCount=count(array_keys($cateIdArr, $v['cate_id']));
                    $v['index'] = $cateCount + 1;
                }else{
                    $v['index'] = $index.'.'.$count;
                    $count++;
                }
                $v['name'] = $v['index'].' '.$v['name'];
                if($v['level']==1){
                    $v['name_tip'] = $v['index_tip'].' '.$v['name'];  //增加一个带下标的虚假name
                }
                $tree[]=$v;
                array_push($cateIdArr,$v['cate_id']);
            }
        }
        if($tree){
            foreach ($tree as $k => &$v){
                $childTree = self::recursionTree($list,$v['id'],$v['index']);
                if($childTree != null){
                    $tree[$k][$children]=$childTree;
                }
            }
        }
        return $tree;
    }



    /**
     * 将tree的树转成list
     * @param array $tree
     * @param string $children
     * @return array
     */
    public function treeToArray(array $tree = [], string $children = 'children'):array
    {
        $list = [];
        if ($tree && is_array($tree)){
            foreach ($tree as $k => $v) {
                $arrTmp = $v;
                unset($arrTmp[$children]);
                $list[] = $arrTmp;
                if (!empty($v[$children])) {
                    $arrTmp = self::treeToArray($v[$children]);
                    $list = array_merge($list, $arrTmp);
                }
            }
        }
        return $list;
    }



    /**
     * 将list转换成tree（root根集合）
     * @param array $list
     * @param string $id
     * @param string $pid
     * @param string $child
     * @param int $root
     * @return array
     */
    public function arrayToTreeByRoot(array $list, string $id='id', string $pid = 'pid', string $child = 'children',int $root = 0):array
    {
        // 创建Tree
        $tree = array();
        if($list && is_array($list)) {
            // 创建基于主键的数组引用
            $refer = array();
            foreach ($list as $key => $data) {
                $refer[$data[$id]] =& $list[$key];
            }
            foreach ($list as $key => $data) {
                // 判断是否存在parent
                $parentId =  $data[$pid];
                if ($root == $parentId) {
                    $tree[] =& $list[$key];
                }else{
                    if (isset($refer[$parentId])) {
                        $parent =& $refer[$parentId];
                        $parent[$child][] =& $list[$key];
                    }
                }
            }
        }
        return $tree;
    }



    /**
     * 计算两点地理坐标之间的距离
     * @param float $longitude1 起点经度
     * @param float $latitude1 起点纬度
     * @param float $longitude2 终点经度
     * @param float $latitude2 终点纬度
     * @param int   $unit    单位 1:米 2:公里
     * @param float $decimal  精度 保留小数位数
     */
    public function getDistance(float $longitude1,float $latitude1,float $longitude2,float $latitude2,int $unit=2,float $decimal=2):float
    {
        $EARTH_RADIUS = 6370.996; // 地球半径系数
        $PI = 3.1415926;

        $radLat1 = $latitude1 * $PI / 180.0;
        $radLat2 = $latitude2 * $PI / 180.0;

        $radLng1 = $longitude1 * $PI / 180.0;
        $radLng2 = $longitude2 * $PI /180.0;

        $a = $radLat1 - $radLat2;
        $b = $radLng1 - $radLng2;

        $distance = 2 * asin(sqrt(pow(sin($a/2),2) + cos($radLat1) * cos($radLat2) * pow(sin($b/2),2)));
        $distance = $distance * $EARTH_RADIUS * 1000;

        if($unit==2){
            $distance = $distance / 1000;
        }
        return round($distance, $decimal);
    }


}
